﻿using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using DomainDriver.DomainModeling.DomainObjects;
using DomainDriver.DomainModeling.Queries;
using DomainDriver.DomainModeling.StorageManagers.DataSetStorage;
using DomainDriver.DomainModeling.StorageManagers.EntityFrameworkStorage;
using AutomotivePricingModel.ValueObjects.YearlyFinancialAssessments;
using ObjectRow = AutomotivePricingModel.AutomotivePricingDataSet.VehiclesRow;

namespace AutomotivePricingModel.Vehicles
{
    public partial class Vehicle : IEfEntity<Guid>, IEfAggregate<Vehicle>, IDataSetSerializable<AutomotivePricingDataSet, DataRow>
    {
        #region ADO .NET Entities Framework Storage

        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.None)]
        public Guid EF_Id
        {
            get { return m_Key; }
            set { m_Key = value; }
        }

        [Required]
        public OptionsConfiguration EF_OptionsConfiguration
        {
            get { return m_OptionsConfiguration; }
            set { m_OptionsConfiguration = value; }
        }

        [Required]
        //[Association("Assc_Vehicle_YearlyFinancialAssessment", "EF_PriceHistoryId", "EF_YearlyFinancialAssessmentId")]
        //[ForeignKey("EF_PriceHistory")]
        public Nullable<Guid> EF_PriceHistoryId
        {
            get { return m_PriceHistoryId; }
            set { m_PriceHistoryId = value; }
        }

        [NotMapped]
        public YearlyFinancialAssessment EF_PriceHistory
        {
            get
            {
                if (!m_PriceHistoryId.HasValue)
                { return null; }
                if (!m_PriceHistory.Key.Equals(m_PriceHistoryId.Value))
                { return null; }
                return m_PriceHistory;
            }
            set
            {
                if (!m_PriceHistoryId.HasValue)
                { throw new ApplicationException(); }
                if (m_PriceHistoryId.Value != value.Key)
                { throw new ApplicationException(); }
                m_PriceHistory = value;
            }
        }

        ICollection<QuerySpecification> IEfAggregate<Vehicle>.OverriddenQueries
        {
            get { return new List<QuerySpecification>(); }
        }

        bool IEfAggregate<Vehicle>.SearchNestedAggregateValues(DbContext context, IParameterizedQuery<Vehicle> query)
        {
            throw new NotImplementedException();
        }

        protected static readonly string YearlyFinancialAssessmentName = (typeof(YearlyFinancialAssessment)).Name;
        void IEfAggregate<Vehicle>.ReadNestedAggregateValues(DbContext context, object rootQueryPredicate, Dictionary<string, object> batchReadState)
        {
            LoadNestedBatchReadState(context, rootQueryPredicate, batchReadState);

            IQueryable<YearlyFinancialAssessment> yearlyFinancialAssessmentResults = (batchReadState[YearlyFinancialAssessmentName] as IQueryable<YearlyFinancialAssessment>).Where<YearlyFinancialAssessment>((YearlyFinancialAssessment yfa) => (yfa.EF_Id == this.m_PriceHistoryId));
            List<YearlyFinancialAssessment> yearlyFinancialAssessmentList = new List<YearlyFinancialAssessment>(yearlyFinancialAssessmentResults);
            this.EF_PriceHistory = yearlyFinancialAssessmentList.FirstOrDefault();
            if (this.EF_PriceHistory != null)
            { this.EF_PriceHistory.ReadNestedAggregateValues(context, rootQueryPredicate, batchReadState); }
        }

        private void LoadNestedBatchReadState(DbContext context, object rootQueryPredicate, Dictionary<string, object> batchReadState)
        {
            if (batchReadState.ContainsKey(YearlyFinancialAssessmentName))
            { return; }

            string vehicleName = (typeof(Vehicle)).Name;
            IEnumerable<Vehicle> vehicles = batchReadState[vehicleName] as IEnumerable<Vehicle>;
            IEnumerable<Guid> vehicleGuids = vehicles.Select((Vehicle veh) => veh.EF_Id);

            List<Guid> yearlyFinancialAssessmentGuids = new List<Guid>();
            List<Vehicle> vehicleList = new List<Vehicle>(vehicles);
            foreach (Vehicle vehicle in vehicleList)
            {
                if (!vehicle.EF_PriceHistoryId.HasValue)
                { continue; }

                yearlyFinancialAssessmentGuids.Add(vehicle.EF_PriceHistoryId.Value);
            }

            Expression<Func<YearlyFinancialAssessment, bool>> yearlyFinancialAssessmentPredicate = null;
            yearlyFinancialAssessmentPredicate = ((YearlyFinancialAssessment yfa) => yearlyFinancialAssessmentGuids.Contains(yfa.EF_Id));
            IQueryable<YearlyFinancialAssessment> yearlyFinancialAssessmentResults = context.Set<YearlyFinancialAssessment>().Where(yearlyFinancialAssessmentPredicate);
            batchReadState.Add(YearlyFinancialAssessmentName, yearlyFinancialAssessmentResults);
        }

        void IEfAggregate<Vehicle>.AddNestedAggregateValues(DbContext context)
        {
            Expression<Func<ICollection<YearlyFinancialAssessment>>> yearlyFinancialAssessmentsGetterExpression = () => (EfAggregateUtilities.ConvertSingleNestedObjectToList(this.EF_PriceHistory));
            EfAggregateUtilities.AddNestedValues<YearlyFinancialAssessment>(context, yearlyFinancialAssessmentsGetterExpression);
        }

        void IEfAggregate<Vehicle>.UpdateNestedAggregateValues(DbContext context, Vehicle originalAggregateRoot)
        {
            Expression<Func<YearlyFinancialAssessment, Guid>> yearlyFinancialAssessmentKeyGetterExpression = ((YearlyFinancialAssessment nestedObj) => (nestedObj.EF_Id));
            Expression<Func<YearlyFinancialAssessment, YearlyFinancialAssessment, bool>> yearlyFinancialAssessmentMatchExpression = ((YearlyFinancialAssessment obj1, YearlyFinancialAssessment obj2) => ((obj1.EF_Id == obj2.EF_Id)));
            Expression<Func<Vehicle, IDictionary<Guid, YearlyFinancialAssessment>>> yearlyFinancialAssessmentDictionaryGetterExpression = ((Vehicle agg) => (EfAggregateUtilities.ConvertSingleNestedObjectToDictionary(agg.EF_PriceHistory, yearlyFinancialAssessmentKeyGetterExpression)));
            EfAggregateUtilities.UpdateNestedValues<Vehicle, Guid, YearlyFinancialAssessment>(context, this, originalAggregateRoot, yearlyFinancialAssessmentKeyGetterExpression, yearlyFinancialAssessmentMatchExpression, yearlyFinancialAssessmentDictionaryGetterExpression);
        }

        void IEfAggregate<Vehicle>.DeleteNestedAggregateValues(DbContext context)
        {
            Expression<Func<YearlyFinancialAssessment, bool>> yearlyFinancialAssessmentDeleteQueryExpression = ((YearlyFinancialAssessment yfa) => (yfa.EF_Id == this.EF_PriceHistoryId));
            EfAggregateUtilities.DeleteNestedValues(context, yearlyFinancialAssessmentDeleteQueryExpression);
        }

        #endregion

        #region AutomotivePricingDataSet Serialization

        string IDataSetSerializable<AutomotivePricingDataSet, DataRow>.ObjectTableName
        {
            get { return AutomotivePricingDataSet.SchemaHelper.Vehicles.TableName; }
        }

        string IDataSetSerializable<AutomotivePricingDataSet, DataRow>.KeyColumnName
        {
            get { return AutomotivePricingDataSet.SchemaHelper.Vehicles.EF_VehicleIdColumn.ColumnName; }
        }

        void IDataSetSerializable<AutomotivePricingDataSet, DataRow>.Serialize(AutomotivePricingDataSet datasource, DataRow dataRow)
        {
            ObjectRow objectRecord = dataRow as ObjectRow;

            objectRecord.EF_VehicleId = m_Key;
            objectRecord.SerializeNullableField<int>(m_ModelYear, AutomotivePricingDataSet.SchemaHelper.Vehicles.ModelYearColumn);
            objectRecord.SerializeNullableField<Guid>(m_ParentProductionModel, AutomotivePricingDataSet.SchemaHelper.Vehicles.ParentProductionModelColumn);
            objectRecord.SerializeNullableField<Guid>(m_PriceHistoryId, AutomotivePricingDataSet.SchemaHelper.Vehicles.EF_PriceHistoryIdColumn);

            objectRecord.EF_OptionsConfiguration_EF_BodyStyle = m_OptionsConfiguration.EF_BodyStyle;
            objectRecord.EF_OptionsConfiguration_EF_DriveTrainType = m_OptionsConfiguration.EF_DriveTrainType;
            objectRecord.EF_OptionsConfiguration_EF_TransmissionType = m_OptionsConfiguration.EF_TransmissionType;
            objectRecord.EF_OptionsConfiguration_EngineSizeInLiters = m_OptionsConfiguration.EngineSizeInLiters;
            objectRecord.EF_OptionsConfiguration_HasNavigationSystem = m_OptionsConfiguration.HasNavigationSystem;
            objectRecord.EF_OptionsConfiguration_NumberOfCylinders = m_OptionsConfiguration.NumberOfCylinders;
            objectRecord.EF_OptionsConfiguration_NumberOfSpeeds = m_OptionsConfiguration.NumberOfSpeeds;

            DataRow nestedObjectRow = datasource.Tables[m_PriceHistory.ObjectTableName].NewRow();
            m_PriceHistory.Serialize(datasource, nestedObjectRow);
        }

        void IDataSetSerializable<AutomotivePricingDataSet, DataRow>.Deserialize(AutomotivePricingDataSet datasource, DataRow dataRow)
        {
            ObjectRow objectRecord = dataRow as ObjectRow;

            m_Key = objectRecord.EF_VehicleId;
            objectRecord.DeserializeNullableField<int>(out m_ModelYear, AutomotivePricingDataSet.SchemaHelper.Vehicles.ModelYearColumn);
            objectRecord.DeserializeNullableField<Guid>(out m_ParentProductionModel, AutomotivePricingDataSet.SchemaHelper.Vehicles.ParentProductionModelColumn);
            objectRecord.DeserializeNullableField<Guid>(out m_PriceHistoryId, AutomotivePricingDataSet.SchemaHelper.Vehicles.EF_PriceHistoryIdColumn);

            m_OptionsConfiguration.EF_BodyStyle = objectRecord.EF_OptionsConfiguration_EF_BodyStyle;
            m_OptionsConfiguration.EF_DriveTrainType = objectRecord.EF_OptionsConfiguration_EF_DriveTrainType;
            m_OptionsConfiguration.EF_TransmissionType = objectRecord.EF_OptionsConfiguration_EF_TransmissionType;
            m_OptionsConfiguration.EngineSizeInLiters = objectRecord.EF_OptionsConfiguration_EngineSizeInLiters;
            m_OptionsConfiguration.HasNavigationSystem = objectRecord.EF_OptionsConfiguration_HasNavigationSystem;
            m_OptionsConfiguration.NumberOfCylinders = objectRecord.EF_OptionsConfiguration_NumberOfCylinders;
            m_OptionsConfiguration.NumberOfSpeeds = objectRecord.EF_OptionsConfiguration_NumberOfSpeeds;

            m_PriceHistory.Deserialize(datasource, objectRecord.YearlyFinancialAssessmentsRow);
        }

        #endregion
    }
}